package com.example.sign.util;

import com.example.sign.core.P7SignGenerator;
import org.bouncycastle.asn1.ASN1UTCTime;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;

import static com.example.sign.constant.P7Constant.P7_CERT_BEGIN_STR;
import static com.example.sign.constant.P7Constant.P7_CERT_END_STR;
import static com.example.sign.util.HashUtil.getDigest;


/**
 * @author benjamin_5
 * @Description p7签名工具类
 */
public class P7SignUtil {

    /**
     * 签名生成
     *
     * @param data
     * @return
     * @throws Exception
     */
    public static String createSign(byte[] data) throws Exception {
        return P7SignGenerator.generate(data);
    }

    /**
     * 验签
     *
     * @param dataBytes 验证数据
     * @param p7Sign    p7签名
     * @param cert      公钥证书
     * @throws Exception
     */
    public static SignReturn verifySign(byte[] dataBytes, String p7Sign, String cert) throws Exception {
        // 加载P7签名数据
        byte[] signature = Base64.getDecoder().decode(p7Sign);
        CMSSignedData signedData = new CMSSignedData(signature);
        // 验证签名
        SignerInformationStore signerInfos = signedData.getSignerInfos();
        SignerInformation signerInfo = signerInfos.getSigners().iterator().next();
        // 验证签名的证书
        X509Certificate certificate = generateCert(cert);
        JcaX509CertificateHolder certHolder = new JcaX509CertificateHolder(certificate);
        SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder().build(certHolder);
        // 验证签名合法性
        boolean verify = signerInfo.verify(verifier);
        SignReturn signReturn = null;
        if (!verify) {
            signReturn = new SignReturn(false, "签名不合法");
            return signReturn;
        }
        signReturn = new SignReturn();
        // 获取签名创建时间
        AttributeTable signedAttributes = signerInfo.getSignedAttributes();
        org.bouncycastle.asn1.cms.Attribute signingTimeAttribute = signedAttributes.get(CMSAttributes.signingTime);
        ASN1UTCTime signingTime = (ASN1UTCTime) signingTimeAttribute.getAttributeValues()[0];
        signReturn.setSignCreateTime(signingTime.getDate());
        // 获取签名者的DN
        X500Name signerDN = signerInfo.getSID().getIssuer();
        signReturn.setSignCreatorInfo(signerDN.toString());
        // 验证数据一致性
        boolean dataValid = Arrays.equals(signerInfo.getContentDigest(), getDigest(dataBytes));
        signReturn.setVerifyResult(dataValid);
        signReturn.setVerifyMessage(dataValid ? "验签通过" : "数据不一致");
        return signReturn;
    }

    /**
     * 根据公钥证书字符串生成X509Certificate对象
     *
     * @param cert
     * @return
     * @throws Exception
     */
    public static X509Certificate generateCert(String cert) throws Exception {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        InputStream inputStream = null;
        // 添加上X.509 约束语
        if (!cert.startsWith(P7_CERT_BEGIN_STR)) {
            cert = P7_CERT_BEGIN_STR + cert;
        }
        if (!cert.endsWith(P7_CERT_END_STR)) {
            cert = cert + P7_CERT_END_STR;
        }
        X509Certificate certificate = null;
        try {
            inputStream = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8));
            certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
        } catch (Exception e) {
            throw e;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
        return certificate;
    }

    public static class SignReturn{
        /**
         * 验签结果
         */
        private boolean verifyResult;
        /**
         * 验签结果描述
         */
        private String verifyMessage;
        /**
         * 签名创建时间
         */
        private Date signCreateTime;
        /**
         * 签名创建者信息
         */
        private String signCreatorInfo;

        public SignReturn() {
        }

        public SignReturn(boolean verifyResult, String verifyMessage) {
            this.verifyResult = verifyResult;
            this.verifyMessage = verifyMessage;
        }

        public boolean isVerifyResult() {
            return verifyResult;
        }

        public void setVerifyResult(boolean verifyResult) {
            this.verifyResult = verifyResult;
        }

        public Date getSignCreateTime() {
            return signCreateTime;
        }

        public void setSignCreateTime(Date signCreateTime) {
            this.signCreateTime = signCreateTime;
        }

        public String getSignCreatorInfo() {
            return signCreatorInfo;
        }

        public void setSignCreatorInfo(String signCreatorInfo) {
            this.signCreatorInfo = signCreatorInfo;
        }

        public String getVerifyMessage() {
            return verifyMessage;
        }

        public void setVerifyMessage(String verifyMessage) {
            this.verifyMessage = verifyMessage;
        }

        @Override
        public String toString() {
            return "验签结果=" + verifyResult +
                    ", 验签信息='" + verifyMessage + '\'' +
                    ", 签名创建时间=" + signCreateTime +
                    ", 签名颁布者='" + signCreatorInfo ;
        }
    }

}
